package com.myzl.coal.client.parse;

import com.myzl.coal.client.CacheData;
import com.myzl.coal.client.config.LocalCommunicationParse;
import com.myzl.coal.domain.AgreementContrast;
import com.myzl.coal.domain.Device;
import com.myzl.coal.domain.RealTimeData;
import com.myzl.coal.domain.dto.RealTimeDataDTO;
import com.myzl.coal.dto.CmdCallBackDTO;
import com.myzl.coal.dto.CmdDTO;
import com.myzl.coal.dto.SendCmdDTO;
import com.myzl.coal.mongo.component.MongoComponent;
import com.myzl.coal.mq.MqService;
import com.myzl.coal.service.IAgreementContrastService;
import com.myzl.coal.service.IRealTimeDataService;
import com.myzl.coal.service.MongoHistoryDataMapper;
import com.myzl.coal.service.protocol.CmdService;
import com.myzl.common.constant.DeviceConstants;
import com.myzl.common.core.domain.entity.SysDictData;
import com.myzl.common.enums.DeviceCommandEnum;
import com.myzl.common.enums.DeviceTypeEnum;
import com.myzl.common.enums.PlcWriteEnum;
import io.netty.channel.ChannelHandler;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.myzl.coal.utils.HexUtil.*;


/**
 * @author 91941
 */
@Component
@ChannelHandler.Sharable
@Data
@Slf4j(topic = "admin-collect")
@LocalCommunicationParse(deviceType = DeviceConstants.DOUBLE_ORDER)
public class DoubleOrderParse extends CommonParseService {

    private final IAgreementContrastService agreementContrastService;
    private final IRealTimeDataService realTimeDataService;
    private final CmdService cmdService;
    private final CacheData cacheData;
    private final MqService mqService;
    private final MongoComponent mongoComponent;
    private final MongoHistoryDataMapper historyDataMapper;

    private BigDecimal offset;
    private BigDecimal maxValue;

    /**
     * 客户端发消息会触发
     */
    @Override
    public void channelRead(Object msg, Integer deviceId) {
        try {
            byte[] dataBytes = (byte[]) msg;
            String dataStr = bytesToHexString(dataBytes);
            log.info("DoubleOrder receive Data:{}", dataStr);

            String num = getString(dataBytes, 0, 2);
            int length = getInt(dataBytes, 4, 2);
            int deviceNum = getInt(dataBytes, 6, 1);
            int fun = getInt(dataBytes, 7, 1);
            int lenAfter = getInt(dataBytes, 8, 1);

            SendCmdDTO sendCmdDTO = cmdService.getSendCmd(deviceId, num);
            log.info("DoubleOrder cmd :{}", sendCmdDTO);
            if (Objects.isNull(sendCmdDTO)) {
                log.info("信息不匹配");
                return;
            }

            CmdCallBackDTO cmdCallBackDTO = new CmdCallBackDTO();
            switch (fun) {
                case 03:
                    List<BigDecimal> value = new ArrayList<>();
                    List<RealTimeDataDTO> realTimeDataListAdd = new ArrayList<>();
                    BigDecimal thousand = BigDecimal.valueOf(1000);
                    BigDecimal timeBigDecimal = BigDecimal.valueOf(3600);
                    BigDecimal dataValueBigDecimal;

                    List<AgreementContrast> agreementContrastList = agreementContrastService.getListSortAddress();
                    Map<Integer, AgreementContrast> agreementContrastMap = agreementContrastList.stream().collect(Collectors.toMap(AgreementContrast::getAddress, Function.identity()));

                    int addressSend = Integer.parseInt(sendCmdDTO.getAddress(), 16);
                    AgreementContrast agreementContrast = agreementContrastMap.get(addressSend);
                    Integer registerCount = agreementContrast.getRegisterCount();

                    long dataValue = getLong(dataBytes, 9, registerCount * 2);
                    String dataType = agreementContrast.getDataType();
                    if ("float".equalsIgnoreCase(dataType)) {
                        float v = Float.intBitsToFloat((int) dataValue);
                        dataValueBigDecimal = BigDecimal.valueOf(v).setScale(5, RoundingMode.HALF_UP);
                    } else if ("double".equalsIgnoreCase(dataType)) {
                        double v;
                        v = Double.longBitsToDouble(dataValue);
                        dataValueBigDecimal = BigDecimal.valueOf(v).setScale(5, RoundingMode.HALF_UP);
                    } else {
                        dataValueBigDecimal = BigDecimal.valueOf(dataValue);
                    }

                    BigDecimal v;
                    double selectDensity;
                    RealTimeDataDTO realTimeData = new RealTimeDataDTO();
                    RealTimeDataDTO realTimeDataFlow = new RealTimeDataDTO();
                    Map<String, SysDictData> dictMap = CacheData.getStringSysDictDataMap();
                    switch (addressSend) {
                        case 0x2108:
                            value.add(dataValueBigDecimal);
//                            if (maxValue==null) {
//                                String selectMax = historyDataMapper.selectMax(deviceId, DeviceCommandEnum.MAX_VALUE.getCode());
//                                if (selectMax != null) {
//                                    maxValue = new BigDecimal(selectMax);
//                                }else {
//                                    maxValue=BigDecimal.ZERO;
//                                }
//                            }
                            String selectMax = historyDataMapper.selectMax(deviceId, DeviceCommandEnum.MAX_VALUE.getCode());
                            String selectOffset = historyDataMapper.selectMax(deviceId, DeviceCommandEnum.OFFSET.getCode());
                            if (selectOffset != null) {
                                offset = new BigDecimal(selectOffset);
                            } else {
                                offset = BigDecimal.ZERO;
                            }
                            if (selectMax != null) {
                                maxValue = new BigDecimal(selectMax);
                            } else {
                                maxValue = BigDecimal.ZERO;
                            }

                            BigDecimal realDataValue = dataValueBigDecimal.add(offset).setScale(5,RoundingMode.HALF_UP);
                            if (realDataValue.compareTo(maxValue) < 0) {
                                realDataValue = maxValue;
                                offset = maxValue.subtract(dataValueBigDecimal);
                                addData(deviceId, DeviceTypeEnum.DOUBLE_ORDER.getCode(), DeviceCommandEnum.OFFSET.getCode(), offset.toString());
                            } else {
                                maxValue = realDataValue;
                            }
                            addData(deviceId, DeviceTypeEnum.DOUBLE_ORDER.getCode(), DeviceCommandEnum.MAX_VALUE.getCode(), realDataValue.toString());

                            BigDecimal cumulativeFlow = realDataValue.divide(thousand).divide(thousand).divide(thousand, 5, RoundingMode.HALF_UP);
                            RealTimeData params = new RealTimeData();
                            params.setDictValue(agreementContrast.getDictValue());
                            params.setDeviceId(deviceId);


                            realTimeData.setDeviceId(deviceId);
                            realTimeData.setDictValue(DeviceCommandEnum.CUMULATIVE_VOLUME.getCode());
                            realTimeData.setDeviceType(DeviceTypeEnum.DOUBLE_ORDER.getCode());
                            realTimeData.setDataValue(cumulativeFlow.toString());
                            realTimeData.setDataUnit(dictMap.get(DeviceCommandEnum.CUMULATIVE_VOLUME.getCode() + "").getValueUnit());
                            realTimeDataListAdd.add(realTimeData);

//                            selectDensity = cacheData.getRealTimeData(deviceId, DeviceCommandEnum.CURRENT_DENSITY.getCode());
//                            realTimeDataFlow.setDeviceId(deviceId);
//                            realTimeDataFlow.setDictValue(DeviceCommandEnum.CUMULATIVE_FLOW.getCode());
//                            realTimeDataFlow.setDeviceType(DeviceTypeEnum.DOUBLE_ORDER.getCode());
//                            realTimeDataFlow.setDataValue(String.valueOf(cumulativeFlow.multiply(BigDecimal.valueOf(selectDensity)).setScale(2, RoundingMode.HALF_UP).doubleValue()));
//                            realTimeDataFlow.setDataUnit(dictMap.get(DeviceCommandEnum.CUMULATIVE_FLOW.getCode() + "").getValueUnit());
//                            realTimeDataListAdd.add(realTimeDataFlow);


                            break;
                        case 0x2114:
                            value.add(dataValueBigDecimal);
                            v = dataValueBigDecimal.multiply(timeBigDecimal).divide(thousand).divide(thousand).divide(thousand, 2, RoundingMode.HALF_UP);

                            realTimeData.setDeviceId(deviceId);
                            realTimeData.setDictValue(DeviceCommandEnum.CURRENT_VOLUME.getCode());
                            realTimeData.setDeviceType(DeviceTypeEnum.DOUBLE_ORDER.getCode());
                            realTimeData.setDataValue(Double.toString(v.setScale(2, RoundingMode.HALF_UP).doubleValue()));
                            realTimeData.setDataUnit(dictMap.get(DeviceCommandEnum.CURRENT_VOLUME.getCode() + "").getValueUnit());
                            realTimeDataListAdd.add(realTimeData);

                            selectDensity = cacheData.getRealTimeData(deviceId, DeviceCommandEnum.CURRENT_DENSITY.getCode());
                            realTimeDataFlow.setDeviceId(deviceId);
                            realTimeDataFlow.setDictValue(DeviceCommandEnum.CURRENT_FLOW.getCode());
                            realTimeDataFlow.setDeviceType(DeviceTypeEnum.DOUBLE_ORDER.getCode());
                            realTimeDataFlow.setDataValue(Double.toString(v.multiply(BigDecimal.valueOf(selectDensity)).setScale(2, RoundingMode.HALF_UP).doubleValue()));
                            realTimeDataFlow.setDataUnit(dictMap.get(DeviceCommandEnum.CURRENT_FLOW.getCode() + "").getValueUnit());
                            realTimeDataListAdd.add(realTimeDataFlow);
                            break;
                        case 0x2104:
                        case 0x2118:
                            value.add(dataValueBigDecimal.divide(thousand, 5, RoundingMode.HALF_UP));
                            break;
                        case 0x2100:
                        case 0x2110:
                        case 0x2120:
                        case 0x2124:
                        case 0x2128:
                        case 0x212c:
                        case 0x2138:
                        default:
                            value.add(dataValueBigDecimal);
                    }

                    value.forEach(e -> {
                        addData(deviceId, DeviceTypeEnum.DOUBLE_ORDER.getCode(), agreementContrast.getDictValue(), e.toString());
                    });

                    if (!realTimeDataListAdd.isEmpty()) {
                        realTimeDataList.addAll(realTimeDataListAdd);
                    }
                    storageData(mqService, DeviceTypeEnum.DOUBLE_ORDER);

                case 05:
                case 16:
                    BeanUtils.copyProperties(sendCmdDTO, cmdCallBackDTO);
                    cmdCallBackDTO.setStatus(true);
                    cmdCallBackDTO.setCallBackCmd(dataStr);
                    cmdService.success(cmdCallBackDTO);
                    break;
                default:
                    BeanUtils.copyProperties(sendCmdDTO,
                            cmdCallBackDTO);
                    cmdCallBackDTO.setStatus(false);
                    cmdService.fail(cmdCallBackDTO);
            }
        } catch (Exception e) {
            log.info("receive data error:", e);
        }
    }

    @Override
    public void initConnect(Integer deviceId) {
        Device device = cacheData.getDevice(deviceId);
        cmdService.asyncWrite(new CmdDTO(DeviceTypeEnum.DOUBLE_ORDER.getCode(), device.getId(), DeviceCommandEnum.ON_OR_OFF_FLOW.getCode(), Collections.singletonList(PlcWriteEnum.ON.getCode()), DeviceCommandEnum.ON_OR_OFF_FLOW.getInfo()));
    }
}
